home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ms_sh21s.zip / SH210 / SRC / SH2.C < prev    next >
C/C++ Source or Header  |  1992-12-14  |  30KB  |  1,367 lines

  1. /* MS-DOS SHELL - Parser
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited and Charles Forsyth
  4.  *
  5.  * This code is based on (in part) the shell program written by Charles
  6.  * Forsyth and is subject to the following copyright restrictions:
  7.  *
  8.  * 1.  Redistribution and use in source and binary forms are permitted
  9.  *     provided that the above copyright notice is duplicated in the
  10.  *     source form and the copyright notice in file sh6.c is displayed
  11.  *     on entry to the program.
  12.  *
  13.  * 2.  The sources (or parts thereof) or objects generated from the sources
  14.  *     (or parts of sources) cannot be sold under any circumstances.
  15.  *
  16.  *    $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh2.c,v 2.3 1992/11/06 10:03:44 istewart Exp $
  17.  *
  18.  *    $Log: sh2.c,v $
  19.  *    Revision 2.3  1992/11/06  10:03:44  istewart
  20.  *    214 Beta test updates
  21.  *
  22.  *    Revision 2.3  1992/11/06  10:03:44  istewart
  23.  *    214 Beta test updates
  24.  *
  25.  *    Revision 2.2  1992/09/03  18:54:45  istewart
  26.  *    Beta 213 Updates
  27.  *
  28.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  29.  *    211 Beta updates
  30.  *
  31.  *    Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
  32.  *    MS-Shell 2.0 Baseline release
  33.  *
  34.  */
  35.  
  36. #include <sys/types.h>
  37. #include <stdio.h>
  38. #include <stddef.h>
  39. #include <signal.h>
  40. #include <errno.h>
  41. #include <setjmp.h>
  42. #include <string.h>
  43. #include <ctype.h>
  44. #include <unistd.h>
  45. #include <limits.h>
  46. #include <dirent.h>
  47. #ifdef OS2
  48. #define INCL_DOSSESMGR
  49. #include <os2.h>
  50. #endif
  51. #include "sh.h"
  52.  
  53. /*
  54.  * shell: syntax (C version)
  55.  */
  56.  
  57. #define PARSE_WORD        256
  58. #define PARSE_LOGICAL_AND    257
  59. #define PARSE_LOGICAL_OR    258
  60. #define PARSE_BREAK        259
  61. #define PARSE_IF        260
  62. #define PARSE_THEN        261
  63. #define PARSE_ELSE        262
  64. #define PARSE_ELIF        263
  65. #define PARSE_FI        264
  66. #define PARSE_CASE        265
  67. #define PARSE_ESAC        266
  68. #define PARSE_FOR        267
  69. #define PARSE_WHILE        268
  70. #define PARSE_UNTIL        269
  71. #define PARSE_DO        270
  72. #define PARSE_DONE        271
  73. #define PARSE_IN        272
  74. #define PARSE_SELECT        273
  75. #define PARSE_FUNCTION        274
  76. #define PARSE_EXPRESSION    275
  77. #define PARSE_TEST        276
  78. #define YYERRCODE        300
  79.  
  80. /* Static data */
  81.  
  82. static bool        AtStartOfList;
  83. static int        PeekSymbol;        /* Look ahead symbol    */
  84. static bool        AllowFunctions;        /* Allow functions    */
  85. static bool        InAlias;        /* In an alias already    */
  86. static bool        AliasContinuationAllowed;
  87. static bool        ParseErrorDectected;    /* Parse error occured    */
  88. static int        CurrentIOUnit = IODEFAULT;
  89.  
  90. static union {
  91.     char    *String;
  92.     int        Integer;
  93. }    CurrentParseObject;
  94.  
  95. static char        *SyntaxErrorMessage = "syntax error";
  96. static char        *MissingEndMessage = "no closing x";
  97. static char        *UnexpectCMessage = "unexpected closing x";
  98.  
  99. static bool near    ProcessWordObject (C_Op **, bool);
  100. static C_Op * near    ScanPipeSyntax (bool);
  101. static C_Op * near    ScanAndOrSyntax (void);
  102. static C_Op * near    CommandList (bool);
  103. static bool near    SynchroniseIOList (bool);
  104. static void near    CheckNextSymbolIS (int, bool);
  105. static C_Op * near    ScanSimpleCommand (void);
  106. static C_Op * near    ScanNestedCommand (int, int);
  107. static C_Op * near    GetNextCommand (bool);
  108. static C_Op * near    GetDoDoneCommandList (bool);
  109. static C_Op * near    ThenPartList (void);
  110. static C_Op * near    ElsePartList (void);
  111. static C_Op * near    CaseList (void);
  112. static C_Op * near    CaseListEntries (void);
  113. static char ** near    GetCasePatterns (void);
  114. static char ** near    GetINWordList (void);
  115. static C_Op * near    GenerateListNode (C_Op *, C_Op *);
  116. static C_Op * near    InitialiseTreeNode (int, C_Op *, C_Op *, char **);
  117. static char ** near    CopyWordList (void);
  118. static IO_Actions * near AddIOAction (int, int, char *);
  119. static void near    ParseError (char *);
  120. static int near        GetNextSymbol (bool);
  121. static int near        CheckForDoubleCharacterCommands (int);
  122. static void near    GetCurrentIOAction (int);
  123. static char * near    CreateTreeNode (size_t);
  124. static int        Alias_GetNextCharacter (IO_State *);
  125. static int near        MC_Error (int, int, char *);
  126. static C_Op * near    ParseExpression (int, char *);
  127.  
  128. /*
  129.  * Parse - main function
  130.  */
  131.  
  132. C_Op    *BuildParseTree (void)
  133. {
  134.     C_Op    *outtree;
  135.  
  136.     AtStartOfList = TRUE;
  137.     PeekSymbol = 0;
  138.     ParseErrorDectected = FALSE;
  139.     LastUserPrompt = PS1;
  140.  
  141. /* Initialise alias information */
  142.  
  143.     InAlias = FALSE;
  144.     AliasContinuationAllowed = FALSE;
  145.  
  146. /* Build the tree */
  147.  
  148.     outtree = CommandList (TRUE);
  149.     CheckNextSymbolIS (CHAR_NEW_LINE, FALSE);
  150.  
  151.     return ParseErrorDectected ? (C_Op *)NULL : outtree;
  152. }
  153.  
  154. static C_Op * near ScanPipeSyntax (bool IgnoreNewLines)
  155. {
  156.     register C_Op    *t, *p;
  157.     register int    c;
  158.  
  159.     if ((t = GetNextCommand (IgnoreNewLines)) != (C_Op *)NULL)
  160.     {
  161.     AllowFunctions = FALSE;
  162.  
  163.     while ((c = GetNextSymbol (FALSE)) == '|')
  164.     {
  165.         if ((p = GetNextCommand (TRUE)) == (C_Op *)NULL)
  166.         ParseError (SyntaxErrorMessage);
  167.  
  168. /* shell statement */
  169.  
  170.         if ((t->type != TPAREN) && (t->type != TCOM))
  171.         t = InitialiseTreeNode (TPAREN, t, NOBLOCK, NOWORDS);
  172.  
  173.         t = InitialiseTreeNode (TPIPE, t, p, NOWORDS);
  174.     }
  175.  
  176.     PeekSymbol = c;
  177.     }
  178.  
  179.     return t;
  180. }
  181.  
  182. static C_Op * near ScanAndOrSyntax (void)
  183. {
  184.     register C_Op    *t, *p;
  185.     register int    c;
  186.  
  187.     if ((t = ScanPipeSyntax (FALSE)) != (C_Op *)NULL)
  188.     {
  189.     AllowFunctions = FALSE;
  190.  
  191.     while (((c = GetNextSymbol (FALSE)) == PARSE_LOGICAL_AND) ||
  192.            (c == PARSE_LOGICAL_OR))
  193.     {
  194.         if ((p = ScanPipeSyntax (TRUE)) == (C_Op *)NULL)
  195.         ParseError (SyntaxErrorMessage);
  196.  
  197.         t = InitialiseTreeNode ((c == PARSE_LOGICAL_AND) ? TAND : TOR,
  198.                     t, p, NOWORDS);
  199.     }
  200.  
  201.     PeekSymbol = c;
  202.     }
  203.  
  204.     return t;
  205. }
  206.  
  207. static C_Op * near CommandList (bool allow)
  208. {
  209.     register C_Op    *t, *p;
  210.     register int    c;
  211.  
  212. /* Functions are only allowed at the start of a line */
  213.  
  214.     AllowFunctions = allow;
  215.  
  216.     if ((t = ScanAndOrSyntax ()) != (C_Op *)NULL)
  217.     {
  218.     AllowFunctions = FALSE;
  219.  
  220.     if ((PeekSymbol = GetNextSymbol (FALSE)) == '&')
  221.         t = InitialiseTreeNode (TASYNC, t, NOBLOCK, NOWORDS);
  222.  
  223.     while (((c = GetNextSymbol (FALSE)) == ';') || (c == '&') ||
  224.            (AllowMultipleLines && (c == CHAR_NEW_LINE)))
  225.     {
  226.         if ((p = ScanAndOrSyntax ()) == (C_Op *)NULL)
  227.         return t;
  228.  
  229.         if ((PeekSymbol = GetNextSymbol (FALSE)) == '&')
  230.         p = InitialiseTreeNode (TASYNC, p, NOBLOCK, NOWORDS);
  231.  
  232.         t = GenerateListNode (t, p);
  233.     }
  234.  
  235.     PeekSymbol = c;
  236.     }
  237.  
  238.     return t;
  239. }
  240.  
  241.  
  242. static bool near SynchroniseIOList (bool IgnoreNewLines)
  243. {
  244.     register IO_Actions    *iop;
  245.     register int    i;
  246.     register int    c;
  247.  
  248.     if (((c = GetNextSymbol (IgnoreNewLines)) != '<') && (c != '>'))
  249.     {
  250.     PeekSymbol = c;
  251.     return FALSE;
  252.     }
  253.  
  254.     i = CurrentParseObject.Integer;
  255.     CheckNextSymbolIS (PARSE_WORD, FALSE);
  256.     iop = AddIOAction (CurrentIOUnit, i, CurrentParseObject.String);
  257.     CurrentIOUnit = IODEFAULT;
  258.  
  259.     if (i & IOHERE)
  260.     SaveHereFileInfo (CurrentParseObject.String, iop);
  261.  
  262.     return TRUE;
  263. }
  264.  
  265. /*
  266.  * Check the next symbol
  267.  */
  268.  
  269. static void near CheckNextSymbolIS (int c, bool IgnoreNewLines)
  270. {
  271.     if ((PeekSymbol = GetNextSymbol (IgnoreNewLines)) != c)
  272.     ParseError (SyntaxErrorMessage);
  273.  
  274.     PeekSymbol = 0;
  275. }
  276.  
  277. static C_Op * near ScanSimpleCommand (void)
  278. {
  279.     C_Op    *t = (C_Op *)NULL;
  280.     bool    FoundAlias;
  281.  
  282.     while (1)
  283.     {
  284.     FoundAlias = InAlias;
  285.     switch (PeekSymbol = GetNextSymbol (FALSE))
  286.     {
  287.         case '<':
  288.         case '>':
  289.         SynchroniseIOList (FALSE);
  290.         break;
  291.  
  292. /* Ok - we've found a word.  If it is the first word - check for an alias */
  293.  
  294.         case PARSE_WORD:
  295.         if (!ProcessWordObject (&t, FoundAlias))
  296.             continue;
  297.  
  298.         break;
  299.  
  300. /* Check for function - name () { word; } */
  301.  
  302.         case CHAR_OPEN_PARATHENSIS:
  303.         if ((t != (C_Op *)NULL) && AllowFunctions &&
  304.             (WordListBlock != (Word_B *)NULL) &&
  305.             (WordListBlock->w_nword == 1))
  306.         {
  307.  
  308. /* Check the format */
  309.  
  310.             PeekSymbol = 0;
  311.             CheckNextSymbolIS (CHAR_CLOSE_PARATHENSIS, FALSE);
  312.             CheckNextSymbolIS (CHAR_OPEN_BRACES, FALSE);
  313.  
  314. /* Save the function name */
  315.  
  316.             t->str = WordListBlock->w_words[0];
  317.             ReleaseMemoryCell ((void *)WordListBlock);
  318.             WordListBlock = (Word_B *)NULL;
  319.             t->type = TFUNC;
  320.  
  321. /* Get the function commands */
  322.  
  323.             t->left = ScanNestedCommand (TBRACE, CHAR_CLOSE_BRACES);
  324.             AllowFunctions = FALSE;
  325.             CheckNextSymbolIS (CHAR_NEW_LINE, FALSE);
  326.             PeekSymbol = CHAR_NEW_LINE;
  327.         }
  328.  
  329.         default:
  330.         return t;
  331.     }
  332.     }
  333. }
  334.  
  335. /*
  336.  * Handle Nested thingys - ( and {
  337.  */
  338.  
  339. static C_Op * near ScanNestedCommand (int type, int mark)
  340. {
  341.     register C_Op    *t;
  342.  
  343.     AllowMultipleLines++;
  344.     t = CommandList (FALSE);
  345.     CheckNextSymbolIS (mark, FALSE);
  346.     AllowMultipleLines--;
  347.     return InitialiseTreeNode (type, t, NOBLOCK, NOWORDS);
  348. }
  349.  
  350. /*
  351.  * Get the Next command
  352.  */
  353.  
  354. static C_Op * near GetNextCommand (bool IgnoreNewLines)
  355. {
  356.     register C_Op    *t;
  357.     Word_B        *iosave = IOActionBlock;
  358.     register int    c;
  359.  
  360.     IOActionBlock = (Word_B *)NULL;
  361.  
  362.     if (AllowMultipleLines)
  363.     IgnoreNewLines = TRUE;
  364.  
  365.     while (SynchroniseIOList (IgnoreNewLines))
  366.     IgnoreNewLines = FALSE;
  367.  
  368.     switch (c = GetNextSymbol (IgnoreNewLines))
  369.     {
  370.     default:
  371.         PeekSymbol = c;
  372.  
  373.         if ((t = ScanSimpleCommand ()) == (C_Op *)NULL)
  374.         {
  375.         if (IOActionBlock == (Word_B *)NULL)
  376.             return (C_Op *)NULL;
  377.  
  378.         (t = (C_Op *)CreateTreeNode (sizeof (C_Op)))->type = TCOM;
  379.         }
  380.  
  381.         break;
  382.  
  383.     case PARSE_EXPRESSION:
  384.         AllowMultipleLines++;
  385.         t = ParseExpression (SWT_LET, "let");
  386.         AllowMultipleLines--;
  387.         break;
  388.  
  389.     case PARSE_TEST:
  390.         AllowMultipleLines++;
  391.         t = ParseExpression (SWT_CONDITIONAL, "[[");
  392.         WordListBlock = AddWordToBlock (StringCopy ("]]"), WordListBlock);
  393.         AllowMultipleLines--;
  394.         break;
  395.  
  396.     case CHAR_OPEN_PARATHENSIS:
  397.         t = ScanNestedCommand (TPAREN, CHAR_CLOSE_PARATHENSIS);
  398.         break;
  399.  
  400.     case CHAR_OPEN_BRACES:
  401.         t = ScanNestedCommand (TBRACE, CHAR_CLOSE_BRACES);
  402.         break;
  403.  
  404. /*
  405.  * Format for:    select word in list do .... done
  406.  *         select word do .... done
  407.  *        for word in list do .... done
  408.  *         for word do .... done
  409.  */
  410.  
  411.     case PARSE_FOR:
  412.     case PARSE_SELECT:
  413.         t = (C_Op *)CreateTreeNode (sizeof (C_Op));
  414.         t->type = (c == PARSE_FOR) ? TFOR : TSELECT;
  415.         CheckNextSymbolIS (PARSE_WORD, FALSE);
  416.         AtStartOfList = TRUE;
  417.         t->str = CurrentParseObject.String;
  418.         AllowMultipleLines++;
  419.         t->words = GetINWordList ();    /* Check for in etc    */
  420.  
  421. /* CHeck for "for/select word in word...; do" versus "for/select word do" */
  422.  
  423.         c = GetNextSymbol (FALSE);
  424.  
  425.         if ((t->words == (char **)NULL) && (c != CHAR_NEW_LINE))
  426.         PeekSymbol = c;
  427.  
  428.         else if ((t->words != (char **)NULL) && (c != CHAR_NEW_LINE) &&
  429.              (c != ';'))
  430.         ParseError (SyntaxErrorMessage);
  431.  
  432.         t->left = GetDoDoneCommandList (FALSE);
  433.         AllowMultipleLines--;
  434.         break;
  435.  
  436. /*
  437.  * Format for:  function name { .... }
  438.  */
  439.  
  440.     case PARSE_FUNCTION:
  441.         t = (C_Op *)CreateTreeNode (sizeof (C_Op));
  442.         t->type = TFUNC;
  443.         CheckNextSymbolIS (PARSE_WORD, FALSE);
  444.         t->str = CurrentParseObject.String;
  445.  
  446.         AtStartOfList = TRUE;
  447.         AllowMultipleLines++;
  448.         CheckNextSymbolIS (CHAR_OPEN_BRACES, FALSE);
  449.         t->left = ScanNestedCommand (TBRACE, CHAR_CLOSE_BRACES);
  450.  
  451.         AllowFunctions = FALSE;
  452.         AllowMultipleLines--;
  453.         CheckNextSymbolIS (CHAR_NEW_LINE, FALSE);
  454.         PeekSymbol = CHAR_NEW_LINE;
  455.         break;
  456.  
  457. /*
  458.  * Format for:    while command do ... done
  459.  *         until command do ... done
  460.  */
  461.  
  462.     case PARSE_WHILE:
  463.     case PARSE_UNTIL:
  464.         AllowMultipleLines++;
  465.         t = (C_Op *)CreateTreeNode (sizeof (C_Op));
  466.         t->type = (c == PARSE_WHILE) ? TWHILE : TUNTIL;
  467.         t->left = CommandList (TRUE);
  468.         t->right = GetDoDoneCommandList (TRUE);
  469.         t->words = (char **)NULL;
  470.         AllowMultipleLines--;
  471.         break;
  472.  
  473. /*
  474.  * Format for:    case name in .... esac
  475.  */
  476.  
  477.     case PARSE_CASE:
  478.         (t = (C_Op *)CreateTreeNode (sizeof (C_Op)))->type = TCASE;
  479.         CheckNextSymbolIS (PARSE_WORD, FALSE);
  480.         t->str = CurrentParseObject.String;
  481.         AtStartOfList = TRUE;
  482.         AllowMultipleLines++;
  483.         CheckNextSymbolIS (PARSE_IN, TRUE);
  484.         AtStartOfList = TRUE;
  485.         t->left = CaseList();
  486.         CheckNextSymbolIS (PARSE_ESAC, FALSE);
  487.         AllowMultipleLines--;
  488.         break;
  489.  
  490. /*
  491.  * Format for:    if command then command fi
  492.  *        if command then command else command fi
  493.  *        if command then command elif command then ... else ... fi
  494.  */
  495.  
  496.     case PARSE_IF:
  497.         AllowMultipleLines++;
  498.         (t = (C_Op *)CreateTreeNode (sizeof (C_Op)))->type = TIF;
  499.         t->left = CommandList (FALSE);
  500.         t->right = ThenPartList ();
  501.         CheckNextSymbolIS (PARSE_FI, FALSE);
  502.         AllowMultipleLines--;
  503.         break;
  504.     }
  505.  
  506.     while (SynchroniseIOList (FALSE))
  507.     ;
  508.  
  509. /* Add Word List and IO Actions to Tree Node.  First process the IO Actions */
  510.  
  511.     if (IOActionBlock)
  512.     {
  513.     t->ioact = (IO_Actions **)GetWordList (AddWordToBlock (NOWORD,
  514.                                    IOActionBlock));
  515.     IOActionBlock = (Word_B *)NULL;
  516.     }
  517.  
  518.     else
  519.     t->ioact = (IO_Actions **)NULL;
  520.  
  521.     if (t->type != TCOM)
  522.     {
  523.     if ((t->type != TPAREN) && (t->ioact != (IO_Actions **)NULL))
  524.     {
  525.         t = InitialiseTreeNode (TPAREN, t, NOBLOCK, NOWORDS);
  526.         t->ioact = t->left->ioact;
  527.         t->left->ioact = (IO_Actions **)NULL;
  528.     }
  529.     }
  530.  
  531. /* Terminate Word List */
  532.  
  533.     else
  534.     {
  535.     WordListBlock = AddWordToBlock (NOWORD, WordListBlock);
  536.     t->words = CopyWordList ();
  537.     }
  538.  
  539. /* Restore IO list on exit */
  540.  
  541.     IOActionBlock = iosave;
  542.     return t;
  543. }
  544.  
  545. /*
  546.  * Processing for the do grouping - do ... done
  547.  */
  548.  
  549. static C_Op * near GetDoDoneCommandList (bool onlydone)
  550. {
  551.     register int    c;
  552.     register C_Op    *list;
  553.  
  554.     if (((c = GetNextSymbol (TRUE)) == PARSE_DONE) && onlydone)
  555.     return (C_Op *)NULL;
  556.  
  557.     if (c != PARSE_DO)
  558.     ParseError (SyntaxErrorMessage);
  559.  
  560.     list = CommandList (FALSE);
  561.     CheckNextSymbolIS (PARSE_DONE, FALSE);
  562.     return list;
  563. }
  564.  
  565. /*
  566.  * Handle the then part of an if statement
  567.  */
  568.  
  569. static C_Op * near ThenPartList (void)
  570. {
  571.     register int    c;
  572.     register C_Op    *t;
  573.  
  574.     if ((c = GetNextSymbol (FALSE)) != PARSE_THEN)
  575.     {
  576.     PeekSymbol = c;
  577.     return (C_Op *)NULL;
  578.     }
  579.  
  580.     (t = (C_Op *)CreateTreeNode (sizeof (C_Op)))->type = 0;
  581.  
  582.     if ((t->left = CommandList (TRUE)) == (C_Op *)NULL)
  583.     ParseError (SyntaxErrorMessage);
  584.  
  585.     t->right = ElsePartList ();
  586.     return t;
  587. }
  588.  
  589. /*
  590.  * Handle the else part of an if statement
  591.  */
  592.  
  593. static C_Op * near ElsePartList (void)
  594. {
  595.     register int    c;
  596.     register C_Op    *t;
  597.  
  598.     switch (c = GetNextSymbol (FALSE))
  599.     {
  600.     case PARSE_ELSE:
  601.         if ((t = CommandList (TRUE)) == (C_Op *)NULL)
  602.         ParseError (SyntaxErrorMessage);
  603.  
  604.         return t;
  605.  
  606.     case PARSE_ELIF:
  607.         (t = (C_Op *)CreateTreeNode (sizeof (C_Op)))->type = TELIF;
  608.         t->left = CommandList (FALSE);
  609.         t->right = ThenPartList ();
  610.         return t;
  611.  
  612.     default:
  613.         PeekSymbol = c;
  614.         return (C_Op *)NULL;
  615.     }
  616. }
  617.  
  618. /*
  619.  * Process the CASE statment
  620.  */
  621.  
  622. static C_Op * near CaseList (void)
  623. {
  624.     register C_Op    *t = (C_Op *)NULL;
  625.  
  626.     while ((PeekSymbol = GetNextSymbol (TRUE)) != PARSE_ESAC)
  627.     t = GenerateListNode (t, CaseListEntries ());
  628.  
  629.     return t;
  630. }
  631.  
  632. /*
  633.  * Process an individual case entry: pattern) commands;;
  634.  */
  635.  
  636. static C_Op * near CaseListEntries (void)
  637. {
  638.     register C_Op    *t = (C_Op *)CreateTreeNode (sizeof (C_Op));
  639.  
  640.     t->type = TPAT;
  641.     t->words = GetCasePatterns ();
  642.     CheckNextSymbolIS (CHAR_CLOSE_PARATHENSIS, FALSE);
  643.     t->left = CommandList (TRUE);
  644.  
  645.     if ((PeekSymbol = GetNextSymbol (TRUE)) != PARSE_ESAC)
  646.     CheckNextSymbolIS (PARSE_BREAK, TRUE);
  647.  
  648.     return t;
  649. }
  650.  
  651. /*
  652.  * Get the case pattern - pattern | pattern
  653.  */
  654.  
  655. static char ** near GetCasePatterns (void)
  656. {
  657.     register int    c;
  658.     register bool    IgnoreNewLines;
  659.  
  660.     IgnoreNewLines = TRUE;
  661.  
  662. /* Get each word which is separated by a | */
  663.  
  664.     do
  665.     {
  666.     CheckNextSymbolIS (PARSE_WORD, IgnoreNewLines);
  667.     WordListBlock = AddWordToBlock (CurrentParseObject.String,
  668.                     WordListBlock);
  669.     IgnoreNewLines = FALSE;
  670.     } while ((c = GetNextSymbol (FALSE)) == '|');
  671.  
  672. /* Save the patterns */
  673.  
  674.     PeekSymbol = c;
  675.     WordListBlock = AddWordToBlock (NOWORD, WordListBlock);
  676.     return CopyWordList ();
  677. }
  678.  
  679. /* Handle the in words.... part of a for or select statement.  Get the
  680.  * words and build a list.
  681.  */
  682.  
  683. static char ** near GetINWordList (void)
  684. {
  685.     register int    c;
  686.  
  687. /* Check to see if the next symbol is "in".  If not there are no words
  688.  * following
  689.  */
  690.  
  691.     if ((c = GetNextSymbol (FALSE)) != PARSE_IN)
  692.     {
  693.     PeekSymbol = c;
  694.     return (char **)NULL;
  695.     }
  696.  
  697. /* Yes - get the WORDs following to the next non-word symbol */
  698.  
  699.     AtStartOfList = FALSE;
  700.     while ((c = GetNextSymbol (FALSE)) == PARSE_WORD)
  701.     WordListBlock = AddWordToBlock (CurrentParseObject.String,
  702.                     WordListBlock);
  703.  
  704. /* Check we got something */
  705.  
  706.     if ((WordListBlock == (Word_B *)NULL) || !WordListBlock->w_nword)
  707.     ParseError (SyntaxErrorMessage);
  708.  
  709. /* Terminate the list */
  710.  
  711.     WordListBlock = AddWordToBlock (NOWORD, WordListBlock);
  712.     PeekSymbol = c;
  713.  
  714.     return CopyWordList ();
  715. }
  716.  
  717. /*
  718.  * supporting functions
  719.  */
  720.  
  721. static C_Op * near GenerateListNode (register C_Op *t1, register C_Op *t2)
  722. {
  723.     if (t1 == (C_Op *)NULL)
  724.     return t2;
  725.  
  726.     if (t2 == (C_Op *)NULL)
  727.     return t1;
  728.  
  729.     return InitialiseTreeNode (TLIST, t1, t2, NOWORDS);
  730. }
  731.  
  732. static C_Op * near InitialiseTreeNode (int type, C_Op *t1, C_Op *t2, char **wp)
  733. {
  734.     register C_Op *t = (C_Op *)CreateTreeNode (sizeof (C_Op));
  735.  
  736.     t->type = type;
  737.     t->left = t1;
  738.     t->right = t2;
  739.     t->words = wp;
  740.     return t;
  741. }
  742.  
  743. static struct res {
  744.     char    *r_name;
  745.     int        r_val;
  746. } restab[] = {
  747.     { "for",    PARSE_FOR},        {"case",    PARSE_CASE},
  748.     { "esac",    PARSE_ESAC},        {"while",    PARSE_WHILE},
  749.     { "do",    PARSE_DO},        {"done",    PARSE_DONE},
  750.     { "if",    PARSE_IF},        {"in",        PARSE_IN},
  751.     { "then",    PARSE_THEN},        {"else",    PARSE_ELSE},
  752.     { "elif",    PARSE_ELIF},        {"until",    PARSE_UNTIL},
  753.     { "fi",    PARSE_FI},        {"select",    PARSE_SELECT},
  754.  
  755.     { "||",    PARSE_LOGICAL_OR},    {";;",        PARSE_BREAK},
  756.     { "&&",    PARSE_LOGICAL_AND},    {"{",        CHAR_OPEN_BRACES},
  757.     { "}",    CHAR_CLOSE_BRACES},    {"function",    PARSE_FUNCTION},
  758.     { "((",    PARSE_EXPRESSION},    { "[[",        PARSE_TEST},
  759.  
  760.     { (char *)NULL,    0}
  761. };
  762.  
  763. int LookUpSymbol (register char *n)
  764. {
  765.     register struct res        *rp = restab;
  766.  
  767.     while ((rp->r_name != (char *)NULL) && strcmp (rp->r_name, n))
  768.     rp++;
  769.  
  770.     return rp->r_val;
  771. }
  772.  
  773. static char ** near CopyWordList (void)
  774. {
  775.     register char **wd = GetWordList (WordListBlock);
  776.  
  777.     WordListBlock = (Word_B *)NULL;
  778.     return wd;
  779. }
  780.  
  781. static IO_Actions * near AddIOAction (int u, int f, char *cp)
  782. {
  783.     IO_Actions    *iop = (IO_Actions *)CreateTreeNode (sizeof (IO_Actions));
  784.  
  785.     iop->io_unit = u;
  786.     iop->io_flag = f;
  787.     iop->io_name = cp;
  788.     IOActionBlock = AddWordToBlock ((char *)iop, IOActionBlock);
  789.     return iop;
  790. }
  791.  
  792. static void near ParseError (char *s)
  793. {
  794.     ParseErrorDectected = TRUE;
  795.  
  796.     if (Interactive ())
  797.     {
  798.     AllowMultipleLines = 0;
  799.  
  800.     while ((!CheckForEndOfFile ()) &&
  801.            (GetNextSymbol (FALSE) != CHAR_NEW_LINE))
  802.         continue;
  803.     }
  804.  
  805.     ShellErrorMessage (s);
  806.     TerminateCurrentEnvironment ();
  807. }
  808.  
  809. static int near    GetNextSymbol (bool IgnoreNewLines)
  810. {
  811.     register int    c, c1;
  812.     int            Type;
  813.     bool        atstart;
  814.  
  815.     if ((c = PeekSymbol) > 0)
  816.     {
  817.     PeekSymbol = 0;
  818.  
  819.     if (c == CHAR_NEW_LINE)
  820.         AtStartOfList = TRUE;
  821.  
  822.     return c;
  823.     }
  824.  
  825.     e.linep = e.cline;
  826.     atstart = AtStartOfList;
  827.     AtStartOfList = FALSE;
  828.     CurrentParseObject.Integer = 0;
  829.  
  830. loop:
  831.     while ((c = GetNextCharacter (0)) == CHAR_SPACE || c == CHAR_TAB)
  832.     ;
  833.  
  834.     switch (c)
  835.     {
  836.     default:
  837.         if (isdigit (c))
  838.         {
  839.         ReturnGotCharacter (c1 = GetNextCharacter (0));
  840.  
  841.         if ((c1 == '<') || (c1 == '>'))
  842.         {
  843.             CurrentIOUnit = c - '0';
  844.             goto loop;
  845.         }
  846.  
  847.         *e.linep++ = (char)c;
  848.         c = c1;
  849.         }
  850.  
  851.         break;
  852.  
  853.     case '#':
  854.         while ((c = GetNextCharacter (0)) != 0 && (c != CHAR_NEW_LINE))
  855.         continue;
  856.  
  857.         ReturnGotCharacter (c);
  858.         goto loop;
  859.  
  860.     case 0:
  861.         return c;
  862.  
  863. /* Allow $name, ${name}, $(command) and support $((arthmetic functions)) */
  864.  
  865.     case '$':
  866.         *e.linep++ = (char)c;
  867.  
  868. /* Handle $(..) and $((...)) */
  869.  
  870.         if ((c = GetNextCharacter (0)) == CHAR_OPEN_PARATHENSIS)
  871.         {
  872.         *e.linep++ = (char)c;
  873.  
  874.         if ((c = GetNextCharacter (0)) != CHAR_OPEN_PARATHENSIS)
  875.             Type = SWT_STDOUT;
  876.  
  877.         else
  878.             Type = SWT_EXPRESSION;
  879.  
  880.         if (ScanForEndofWord (c, Type))
  881.             return 0;
  882.  
  883.         goto pack;
  884.         }
  885.  
  886. /* Handle ${...} */
  887.  
  888.         else if (c == CHAR_OPEN_BRACES)
  889.         {
  890.         if (ScanForEndofWord (c, SWT_EVARIABLE))
  891.             return 0;
  892.  
  893.         goto pack;
  894.         }
  895.  
  896.         break;
  897.  
  898.     case CHAR_BACKQUOTE:
  899.     case CHAR_SINGLE_QUOTE:
  900.     case CHAR_DOUBLE_QUOTE:
  901.         if ((c = CollectInputToCharacter (c, c)))
  902.         return c;
  903.  
  904.         goto pack;
  905.  
  906.     case '|':
  907.     case '&':
  908.     case ';':
  909.     case CHAR_OPEN_PARATHENSIS:
  910.         if ((c1 = CheckForDoubleCharacterCommands (c)))
  911.         {
  912.         AliasContinuationAllowed = FALSE;
  913.         AtStartOfList = TRUE;
  914.         return c1;
  915.         }
  916.  
  917.     case CHAR_CLOSE_PARATHENSIS:
  918.         AliasContinuationAllowed = FALSE;
  919.         AtStartOfList = TRUE;
  920.         return c;
  921.  
  922.     case CHAR_NOT:
  923.         AliasContinuationAllowed = FALSE;
  924.         AtStartOfList = TRUE;
  925.         return '|';
  926.  
  927.     case '>':
  928.     case '<':
  929.         AliasContinuationAllowed = FALSE;
  930.         GetCurrentIOAction (c);
  931.         return c;
  932.  
  933.     case CHAR_NEW_LINE:
  934.         GetAllHereFiles ();
  935.         AtStartOfList = TRUE;
  936.  
  937.         if (((AllowMultipleLines || IgnoreNewLines)) && IgnoreNewLines)
  938.         goto loop;
  939.  
  940.         return c;
  941.     }
  942.  
  943.     ReturnGotCharacter (c);
  944.  
  945. pack:
  946.     while (((c = GetNextCharacter (0)) != 0) &&
  947.        (!any ((char)c, "`$ '\"\t;&<>()|^\n")))
  948.     {
  949.     if (e.linep >= e.eline)
  950.         ShellErrorMessage ("word too long");
  951.  
  952.     else
  953.         *e.linep++ = (char)c;
  954.     }
  955.  
  956.     ReturnGotCharacter (c);
  957.  
  958.     if (any ((char)c, spcl2))
  959.     goto loop;
  960.  
  961.     *e.linep++ = '\0';
  962.  
  963.     if (atstart && (c = LookUpSymbol (e.cline)) != 0)
  964.     {
  965.     AliasContinuationAllowed = FALSE;
  966.     AtStartOfList = TRUE;
  967.     return c;
  968.     }
  969.  
  970. /* Otherwise, handle words beginning with a ~ */
  971.  
  972.     if (*e.cline == '~')
  973.     {
  974.     char    *dir;
  975.     int    off = 2;
  976.  
  977. /* OLDPWD or PWD if - or + */
  978.  
  979.     if (*(e.cline + 1) == '+')
  980.         dir = GetVariableAsString (PWDVariable, FALSE);
  981.  
  982.     else if (*(e.cline + 1) == '-')
  983.         dir = GetVariableAsString (OldPWDVariable, FALSE);
  984.  
  985. /* No - use home directory */
  986.  
  987.     else
  988.     {
  989.         dir = GetVariableAsString (HomeVariableName, FALSE);
  990.         off = 1;
  991.     }
  992.  
  993.     CurrentParseObject.String = CreateTreeNode (strlen (e.cline) +
  994.                             strlen (dir));
  995.     strcat (strcpy (CurrentParseObject.String, dir), e.cline + off);
  996.     }
  997.  
  998. /* Otherwise, just save it */
  999.  
  1000.     else
  1001.     CurrentParseObject.String = StringCopy (e.cline);
  1002.  
  1003.     return PARSE_WORD;
  1004. }
  1005.  
  1006. /* Read input until we read the specified end character */
  1007.  
  1008. int CollectInputToCharacter (register int c, register int EndCharacter)
  1009. {
  1010.     *(e.linep++) = (char)c;        /* Save the current character    */
  1011.  
  1012. /* Check for end - matching character */
  1013.  
  1014.     while ((c = GetNextCharacter (EndCharacter)) != EndCharacter)
  1015.     {
  1016.  
  1017. /* End of file - abort    */
  1018.  
  1019.     if (c == 0)
  1020.         return MC_Error (0, EndCharacter, MissingEndMessage);
  1021.  
  1022.     if (c == '\\')
  1023.     {
  1024.         *(e.linep++) = (char)c;
  1025.  
  1026.             if ((c = GetNextCharacter (EndCharacter)) == 0)
  1027.         return MC_Error (0, EndCharacter, MissingEndMessage);
  1028.     }
  1029.  
  1030.     *(e.linep++) = (char)c;
  1031.     }
  1032.  
  1033.     *(e.linep++) = (char)c;
  1034.     return 0;
  1035. }
  1036.  
  1037. /* Check for &&, || and ;; */
  1038.  
  1039. static int near    CheckForDoubleCharacterCommands (register int c)
  1040. {
  1041.     char        s[3];
  1042.     register char    *cp = s;
  1043.  
  1044. /* Get the next character and set up double string.  Look up in valid
  1045.  * operators.  If invalid, unget character
  1046.  */
  1047.  
  1048.     *cp++ = (char)c;
  1049.     *cp++ = (char)GetNextCharacter (0);
  1050.     *cp = 0;
  1051.  
  1052.     if ((c = LookUpSymbol (s)) == 0)
  1053.     ReturnGotCharacter (*--cp);
  1054.  
  1055.     return c;
  1056. }
  1057.  
  1058. /* Process I/O re-direction */
  1059.  
  1060. static void near GetCurrentIOAction (register int ec)
  1061. {
  1062.     register int    c;
  1063.  
  1064. /* Get the next character to see if it is a re-direction character as well */
  1065.  
  1066.     if (((c = GetNextCharacter (0)) == '>') || (c == '<'))
  1067.     {
  1068.  
  1069. /* Check for open in read/write mode */
  1070.  
  1071.     if ((ec == '<') && (c == '>'))
  1072.         CurrentParseObject.Integer = IOWRITE | IOREAD;
  1073.  
  1074. /* Otherwise, we must have a double character */
  1075.  
  1076.     else if (c != ec)
  1077.         ParseError (SyntaxErrorMessage);
  1078.  
  1079.     else
  1080.         CurrentParseObject.Integer = (ec == '>') ? IOWRITE | IOCAT : IOHERE;
  1081.  
  1082.     c = GetNextCharacter (0);
  1083.     }
  1084.  
  1085.     else
  1086.     CurrentParseObject.Integer = (ec == '>') ? IOWRITE : IOREAD;
  1087.  
  1088. /* Check for >|, >&, <& and <<- */
  1089.  
  1090.     if ((c == '-') && (CurrentParseObject.Integer == IOHERE))
  1091.     CurrentParseObject.Integer |= IOTHERE;
  1092.  
  1093.     else if ((c == '|') && (CurrentParseObject.Integer == IOWRITE))
  1094.     CurrentParseObject.Integer |= IOCLOBBER;
  1095.  
  1096.     else if ((c != '&') || (CurrentParseObject.Integer == IOHERE))
  1097.     ReturnGotCharacter (c);
  1098.  
  1099.     else
  1100.     CurrentParseObject.Integer |= IODUP;
  1101. }
  1102.  
  1103. /* Get a new tree leaf structure */
  1104.  
  1105. static char * near CreateTreeNode (size_t size)
  1106. {
  1107.     register char *t;
  1108.  
  1109.     if ((t = AllocateMemoryCell (size)) == (char *)NULL)
  1110.     {
  1111.     ShellErrorMessage ("command line too complicated");
  1112.     TerminateCurrentEnvironment ();
  1113.     }
  1114.  
  1115.     return t;
  1116. }
  1117.  
  1118. /*
  1119.  * Produce the characters from an alias.
  1120.  */
  1121.  
  1122. static int Alias_GetNextCharacter (IO_State *iop)
  1123. {
  1124.     register int    c;
  1125.  
  1126.     if (iop->argp->aword == (char *)NULL)
  1127.     c = 0;
  1128.  
  1129.     else
  1130.     c = *(iop->argp->aword++);
  1131.  
  1132. /* Check for end of alias */
  1133.  
  1134.     if (!c)
  1135.     InAlias = FALSE;
  1136.  
  1137. /* Check for continuation allowed */
  1138.  
  1139.     if ((c == CHAR_SPACE) && !*(iop->argp->aword))
  1140.     AliasContinuationAllowed = TRUE;
  1141.  
  1142.     return c;
  1143. }
  1144.  
  1145. /*
  1146.  * Handler the finding of a word object
  1147.  */
  1148.  
  1149. static bool near ProcessWordObject (C_Op **t, bool FoundAlias)
  1150. {
  1151.     AliasList        *al;
  1152.     char        *val = CurrentParseObject.String;
  1153.  
  1154.     PeekSymbol = 0;
  1155.  
  1156.     if (*t == (C_Op *)NULL)
  1157.     {
  1158.     if (!InAlias && !FoundAlias &&
  1159.         ((al = LookUpAlias (val, TRUE)) != (AliasList *)NULL))
  1160.     {
  1161.         InAlias = TRUE;
  1162.         PUSHIO (aword, al->value, Alias_GetNextCharacter, null);
  1163.         return FALSE;
  1164.     }
  1165.  
  1166. /* No - generate a new node for it */
  1167.  
  1168.     (*t = (C_Op *)CreateTreeNode (sizeof (C_Op)))->type = TCOM;
  1169.     }
  1170.  
  1171. /* Check for AliasContinuation - a space at the end of the alias */
  1172.  
  1173.     else if (AliasContinuationAllowed && !InAlias &&
  1174.          ((al = LookUpAlias (val, TRUE)) != (AliasList *)NULL))
  1175.     {
  1176.     AliasContinuationAllowed = FALSE;
  1177.     InAlias = TRUE;
  1178.     PUSHIO (aword, al->value, Alias_GetNextCharacter, null);
  1179.     return FALSE;
  1180.     }
  1181.  
  1182.     WordListBlock = AddWordToBlock (val, WordListBlock);
  1183.     return TRUE;
  1184. }
  1185.  
  1186. /*
  1187.  * Missing end character
  1188.  */
  1189.  
  1190. static int near MC_Error (int end, int EndCharacter, char *message)
  1191. {
  1192.     ReturnGotCharacter (end);
  1193.     message[strlen (message) - 1] = (char)EndCharacter;
  1194.     ParseError (message);
  1195.     return YYERRCODE;
  1196. }
  1197.  
  1198. /*
  1199.  * Handle the ((....)) case
  1200.  */
  1201.  
  1202. static C_Op * near ParseExpression (int Type, char *Command)
  1203. {
  1204.     C_Op    *t;
  1205.     char    *Start = e.cline;
  1206.  
  1207.     (t = (C_Op *)CreateTreeNode (sizeof (C_Op)))->type = TCOM;
  1208.  
  1209. /* Initialise the input line */
  1210.  
  1211.     e.linep = e.cline;
  1212.  
  1213. /* Collect up to terminating double */
  1214.  
  1215.     if (ScanForEndofWord ((Type == SWT_CONDITIONAL) ? CHAR_SPACE
  1216.                             : CHAR_DOUBLE_QUOTE, Type))
  1217.     return t;
  1218.  
  1219. /* Put the string inside double quotes to stop interpretation */
  1220.  
  1221.     if (Type == SWT_CONDITIONAL)
  1222.     {
  1223.     *(e.linep - 2) = 0;
  1224.     ++Start;
  1225.     }
  1226.  
  1227.     else
  1228.     {
  1229.     *(e.linep - 2) = CHAR_DOUBLE_QUOTE;
  1230.     *(e.linep - 1) = 0;
  1231.     }
  1232.  
  1233. /* Save the command as let and the string itself */
  1234.  
  1235.     WordListBlock = AddWordToBlock (StringCopy (Command), WordListBlock);
  1236.     WordListBlock = AddWordToBlock (StringCopy (Start), WordListBlock);
  1237.     return t;
  1238. }
  1239.  
  1240. /*
  1241.  * Scan for end of either $(...), ${...}, ((....)), [[....]], $((...))
  1242.  *
  1243.  * Return 0 at end of string.
  1244.  */
  1245.  
  1246. static struct SWT_table {
  1247.     char    Start;
  1248.     char    End;
  1249.     bool    Double;
  1250. } SWT_Table [] =
  1251. {
  1252.     { CHAR_OPEN_PARATHENSIS,    CHAR_CLOSE_PARATHENSIS,    FALSE },
  1253.     { CHAR_OPEN_BRACES,        CHAR_CLOSE_BRACES,    FALSE },
  1254.     { CHAR_OPEN_PARATHENSIS,    CHAR_CLOSE_PARATHENSIS,    TRUE },
  1255.     { CHAR_OPEN_BRACKETS,    CHAR_CLOSE_BRACKETS,    TRUE },
  1256.     { CHAR_OPEN_PARATHENSIS,    CHAR_CLOSE_PARATHENSIS,    TRUE },
  1257. };
  1258.  
  1259. int ScanForEndofWord (int Current, int Type)
  1260. {
  1261.     struct SWT_table    *SWTp = &SWT_Table[Type];
  1262.     int            Rchar;
  1263.     int            NewType;
  1264.     bool        FirstTime = FALSE;
  1265.  
  1266. /* Handle some funny cases: () for example */
  1267.  
  1268.     if (Current == SWTp->End)
  1269.     FirstTime = TRUE;
  1270.  
  1271.     else
  1272.     *(e.linep)++ = (char)Current;    /* Save the current character    */
  1273.  
  1274.     while (TRUE)
  1275.     {
  1276.  
  1277. /* Get the next character and check for end of string */
  1278.  
  1279.     if (FirstTime ||
  1280.         ((Current = GetNextCharacter (SWTp->End)) == SWTp->End))
  1281.     {
  1282.         FirstTime = FALSE;
  1283.         *(e.linep++) = (char)Current;
  1284.  
  1285. /* If this is a double, we need both */
  1286.  
  1287.         if (!SWTp->Double)
  1288.         return 0;
  1289.  
  1290.         if ((Current = GetNextCharacter (SWTp->End)) == SWTp->End)
  1291.         {
  1292.         *(e.linep++) = (char)Current;
  1293.         return 0;
  1294.         }
  1295.     }
  1296.  
  1297.     if ((Current == CHAR_SINGLE_QUOTE) || (Current == CHAR_DOUBLE_QUOTE))
  1298.     {
  1299.         if ((Type = CollectInputToCharacter (Current, Current)))
  1300.         return Type;
  1301.  
  1302.         continue;
  1303.     }
  1304.  
  1305. /* Special Processing is required for these */
  1306.  
  1307.     else if ((Current == '$') || (Current == CHAR_OPEN_PARATHENSIS) ||
  1308.          (Current == CHAR_OPEN_BRACKETS))
  1309.     {
  1310.         *(e.linep++) = (char)Current;
  1311.         Rchar = Current;
  1312.         NewType = -1;
  1313.  
  1314. /* Get the next character */
  1315.  
  1316.         if ((Current = GetNextCharacter (0)) == 0)
  1317.         return MC_Error (0, SWTp->End, MissingEndMessage);
  1318.  
  1319.         if (Rchar == '$')
  1320.         {
  1321.         if (Current == CHAR_OPEN_PARATHENSIS)
  1322.         {
  1323.             *(e.linep++) = (char)Current;
  1324.  
  1325.             if ((Current = GetNextCharacter (0)) == 0)
  1326.             return MC_Error (0, SWTp->End, MissingEndMessage);
  1327.  
  1328.             NewType = (Current == CHAR_OPEN_PARATHENSIS)
  1329.                 ? SWT_EXPRESSION : SWT_STDOUT;
  1330.         }
  1331.  
  1332.         else if (Current == CHAR_OPEN_BRACES)
  1333.             NewType = SWT_EVARIABLE;
  1334.         }
  1335.  
  1336.         else if ((Rchar == CHAR_OPEN_PARATHENSIS) &&
  1337.              (Current == CHAR_OPEN_PARATHENSIS))
  1338.             NewType = SWT_LET;
  1339.  
  1340.         else if ((Rchar == CHAR_OPEN_BRACKETS) &&
  1341.              (Current == CHAR_OPEN_BRACKETS))
  1342.             NewType = SWT_CONDITIONAL;
  1343.  
  1344.         if (NewType != -1)
  1345.         {
  1346.         if ((Type = ScanForEndofWord (Current, NewType)))
  1347.             return Type;
  1348.  
  1349.         continue;
  1350.         }
  1351.     }
  1352.  
  1353.     else if (Current == '\\')
  1354.     {
  1355.         *(e.linep++) = (char)Current;
  1356.         Current = GetNextCharacter (0);
  1357.     }
  1358.  
  1359. /* Check for End of file - abort */
  1360.  
  1361.     if (Current == 0)
  1362.         return MC_Error (0, SWTp->End, MissingEndMessage);
  1363.  
  1364.     *(e.linep++) = (char)Current;
  1365.     }
  1366. }
  1367.